﻿using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using WebuSocketCore;
using SimpleJSON;


/// <summary>
/// Version 4.3
/// 修改内容：1.断网重连部分，自带功能修改为关闭连接加手动重新创建
/// webSocket = WebuSocket.Reconnect(webSocket);原语句
/// webSocket.Disconnect();关闭
/// Create();手动重新创建
/// 2.心跳包发送增加try语句，并调整is_heart_link = false;在try之前
/// 3.底部Json2Str增加长度容错
/// ------------------
/// Version 4.2
/// </summary>
public class IOT35 : MonoBehaviour
{
	[Header("基本连接")]
	public string id;
	public string url;

	[Header("登录")]
	public string login_send_iot = "{'type':'conn', 'nm':'***'}";
	public string login_recv_iot = "{'type':'conn-ok'}";

	[Header("断网重连")]
	public bool if_reconnect = true;
	public float reconnect_time = 3;
	public bool if_reconnect_print = false;

	[Header("心跳包")]
	public bool if_heart = true;
	public float heart_time = 60;
	public string heart_send_iot = "t";
	public string heart_recv_iot = "{'type':'t'}";
	public bool if_heart_print = false;

	[Header("回调函数")]
	//登录回调
	public UnityEvent OnLogin;
	private bool is_login = false;
	//连接回调
	public UnityEvent OnConnect;
	private bool is_connect_callback = false;
	//发送回调
	public bool if_send_callback = false;
	public SendEvent OnSend;
	[Serializable]
	public class SendEvent : UnityEvent<string> { }
	//接收回调
	public RecvEvent OnRecv;
	[Serializable]
	public class RecvEvent : UnityEvent<JSONNode> { }
	//关闭回调
	public UnityEvent OnClose;
	private bool is_close_callback = false;

	private WebuSocket webSocket;
	//判断是否处于关闭状态(未创建前状态)
	private bool is_create = false;
	private bool is_connect = false;
	//心跳包是否已经连接
	private bool is_heart_link = true;
	//是否收到
	private bool is_recv = false;
	private string recv;
	
	private void Update()
	{
		if (is_create)
		{
			if (is_recv)
			{
				is_recv = false;
				StartCoroutine(Recv(recv));
			}

			if (is_connect_callback)
			{
				is_connect_callback = false;
				if (OnConnect.GetPersistentEventCount() > 0)
				{
					OnConnect.Invoke();
				}
			}

			if (is_login)
			{
				is_login = false;
				if (OnLogin.GetPersistentEventCount() > 0)
				{
					OnLogin.Invoke();
				}
			}

			if (is_close_callback)
			{
				is_close_callback = false;
				if (OnClose.GetPersistentEventCount() > 0)
				{
					OnClose.Invoke();
				}
			}
		}
	}

	/// <summary>
	/// /创建websocket
	/// </summary>
	public void Create()
	{
		print("WS:" + url);

		if (if_heart)
		{
			heart_recv_iot = FormatIot(heart_recv_iot);
			if (!IsInvoking("WS_Heart"))
				this.InvokeRepeating("WS_Heart", 30f, heart_time);
		}
		if (if_reconnect)
		{
			if (!IsInvoking("WS_ReConnect"))
				this.InvokeRepeating("WS_ReConnect", 5f, reconnect_time);
		}

		login_send_iot = login_send_iot.Replace("***", id);
		login_send_iot = FormatIot(login_send_iot);
		login_recv_iot = FormatIot(login_recv_iot);

		//默认设置，防止启动时旧状态
		is_connect_callback = false;
		is_close_callback = false;
		is_login = false;

		webSocket = new WebuSocket(
			// url.
			url,

			// buffer size.
			1024,

			// handler for connection established to server.
			() =>
			{
				//Debug.Log("connected to websocket echo-server. send hello to echo-server");
				is_connect = true;
				is_connect_callback = true;
				print("wsc连接成功");

				//发送登录信息
				if (id != String.Empty)
				{
					Send(login_send_iot);
				}
				
			},

			// handler for receiving data from server. 
			datas =>
			{
				/*
					this handler is called from system thread. not Unity's main thread.
					and, datas is ArraySegment<byte> x N. 
					SHOULD COPY byte data from datas HERE.
					do not copy ArraySegment<byte> itself.
					these data array will be destroyed soon after leaving this block.
				*/
				while (0 < datas.Count)
				{
					ArraySegment<byte> data = datas.Dequeue();

					byte[] bytes = new byte[data.Count];
					Buffer.BlockCopy(data.Array, data.Offset, bytes, 0, data.Count);

					string message = Encoding.UTF8.GetString(bytes);
					string msg = FormatIot(message);
					//心跳包判断 若{}格式，注意收到单引号与空格
					if (msg.Equals(heart_recv_iot))
					{
						is_heart_link = true;
						if (if_heart_print)
						{
							print("心跳包回馈：" + message);
						}
					}
					//登录
					else if (msg.Equals(login_recv_iot))
					{
						is_login = true;
					}
					else
					{
						is_recv = true;
						recv = msg;
					}

				}
			},
			() =>
			{
				Debug.Log("received server ping. automatically ponged.");
			},
			closeReason =>
			{
				Debug.Log("closed, closeReason:" + closeReason);

				is_connect = false;
				if (is_create)
				{
					is_close_callback = true;
				}
			},
			(errorEnum, exception) =>
			{
				Debug.LogError("error, errorEnum:" + errorEnum + " exception:" + exception);

				is_connect = false;
				if (is_create)
				{
					is_close_callback = true;
				}
			},
			new Dictionary<string, string>
			{
				// set WebSocket connection header parameters here!
			}
		);
		is_create = true;
	}

	/// <summary>
	/// 心跳包循环函数
	/// </summary>
	private void WS_Heart()
	{
		if (if_heart && is_connect)
		{
			if (!is_heart_link)
			{
				is_heart_link = true;
				if (if_heart_print)
				{
					print("未收到心跳包：" + heart_send_iot);
				}
				if (if_reconnect)
				{
					print("心跳包1：" + if_reconnect);
					is_connect = false;
				}
			}
			if (if_heart_print)
			{
				print("发送心跳包:" + heart_send_iot);
			}

			is_heart_link = false;

			try
			{
				webSocket.SendString(heart_send_iot);
			}
			catch (Exception err)
			{
				print("心跳包2：" + err);
				print(err);
			}
			


		}
	}

	/// <summary>
	/// 重新设置心跳包
	/// </summary>
	public void SetHeart(float delay = 0)
	{
		//清理
		if (IsInvoking("WS_Heart"))
		{
			CancelInvoke("WS_Heart");
		}

		if (if_heart)
		{
			if (heart_send_iot != String.Empty && heart_recv_iot != String.Empty && heart_time > 0)
			{
				this.InvokeRepeating("WS_Heart", delay, heart_time);
			}
		}

		print("SetHeart");
	}

	/// <summary>
	/// 断网重连
	/// </summary>
	private void WS_ReConnect()
	{
		if (if_reconnect && !is_connect)
		{
			if (if_reconnect_print)
			{
				print("断网重连");
			}

			//webSocket = WebuSocket.Reconnect(webSocket);
			try
			{
				webSocket.Disconnect();
			}
			catch(Exception err)
			{
				print(err);
			}
			Create();
		}
	}

	/// <summary>
	/// 重新设置断网重连
	/// </summary>
	public void SetReConnect(float delay = 0)
	{
		//清理
		if (IsInvoking("WS_ReConnect"))
		{
			CancelInvoke("WS_ReConnect");
		}

		if (if_reconnect)
		{
			if (reconnect_time > 0)
			{
				this.InvokeRepeating("WS_ReConnect", delay, reconnect_time);
			}
		}

		print("SetReConnect");
	}

	/// <summary>
	/// 发送
	/// </summary>
	public void Send(string msg)
	{
		if (is_connect)
		{
			msg = FormatIot(msg);
			webSocket.SendString(msg);
			if (if_send_callback && OnSend.GetPersistentEventCount() > 0)
			{
				try
				{
					OnSend.Invoke(msg);
				}
				catch (Exception e)
				{
					print("WSC , Send运行错误:" + msg);
					print(e);
				}
			}
		}
	}

	/// <summary>
	/// 发送json格式
	/// </summary>
	/// <param name="json"></param>
	public void Send(JSONNode json)
	{
		string msg = Json2Str(json);
		print("发送：" + msg);
		Send(msg);
	}

	/// <summary>
	/// 收到信息
	/// </summary>
	public IEnumerator Recv(string recv)
	{
		try
		{
			string json_str = recv.Replace('\'', '"');
			JSONNode json = JSON.Parse(json_str);
			if (OnRecv.GetPersistentEventCount() > 0)        
			{
				OnRecv.Invoke(json);
			}
		}
		catch (Exception e)
		{
			print("WSC , Recv运行错误:" + recv);
			print(e);
		}

		yield return true;
	}

	/// <summary>
	/// Iot格式化
	/// </summary>
	/// <returns></returns>
	public string FormatIot(string iot)
	{
		iot = iot.Replace('"', '\'');
		return iot.Replace(" ", "");
	}

	/// <summary>
	/// 关闭连接
	/// </summary>
	public void Close()
	{
		if (is_connect)
		{
			webSocket.Disconnect();
		}
	}

	/// <summary>
	/// 退出
	/// </summary>
	//void OnApplicationQuit()
	//{
	//	try
	//	{
	//		CancelInvoke("WS_Heart");
	//		CancelInvoke("WS_ReConnect");
	//		webSocket.Disconnect();
	//	}
	//	catch (Exception e)
	//	{
	//		print(e);
	//	}
	//}


	/// <summary>
	/// Json转字符串
	/// </summary>
	/// <param name="json"></param>
	/// <returns></returns>
	public string Json2Str(JSONNode json)
	{
		string str = "{";
		foreach (var item in json)
		{
			//print(item);
			str += "'" + item.Key + "':";
			string val = item.Value;
			if (val.Length > 0)
			{
				if (val.Substring(0, 1) == "{" && val.Substring(val.Length - 1, 1) == "}")
				{
					str += item.Value + ",";
				}
				else
				{
					str += "'" + item.Value + "',";
				}
			}
			else
			{
				str += "'',";
			}
			
		}
		str = str.Substring(0, str.Length - 1) + "}";
		return str;
	}
}
